package com.ciei.dpagm.common.converter;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.TableId;
import com.ciei.dpagm.common.enums.JsonConvertFeatureEnum;
import com.ciei.dpagm.util.ConvertUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Map;

/**
 * json转对象转换类，目前仅针对转实体开发
 * @author fangnuozhou
 * @date 2020/08/19
 */
public class JsonConverter<T> {

    /**
     * ID在Map中对应的key，默认为“id”
     */
    private String idKeyInMap;

    /**
     * 转换特性
     */
    private JsonConvertFeatureEnum[] jsonConvertFeatureEnums;

    public JsonConverter() {
        idKeyInMap = "id";
        jsonConvertFeatureEnums = new JsonConvertFeatureEnum[]{};
    }

    public JsonConverter(JsonConvertFeatureEnum... jsonConvertFeatureEnums) {
        idKeyInMap = "id";
        this.jsonConvertFeatureEnums = jsonConvertFeatureEnums;
    }

    /**
     * 将json转换为新的对象
     * @param jsonObject    json数据
     * @param clazz         目标对象类型
     * @return              新生成的对象
     */
    public T convert(JSONObject jsonObject, Class<T> clazz) {
        try {
            T entity = clazz.newInstance();
            return convert(jsonObject, entity);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将json的数据赋值给已有对象
     * @param jsonObject    json数据
     * @param entity        已有对象
     * @return              赋值后的对象
     */
    public T convert(JSONObject jsonObject, T entity) {
        if (jsonObject == null || entity == null) {
            return entity;
        }

        // 是否忽略实体主键
        boolean ignoreEntityPrimaryKey = ArrayUtils.contains(jsonConvertFeatureEnums, JsonConvertFeatureEnum.IGNORE_ENTITY_PRIMARY_KEY);
        // 是否忽略null值或空值
        boolean ignoreNullOrEmpty = ArrayUtils.contains(jsonConvertFeatureEnums, JsonConvertFeatureEnum.IGNORE_NULL_OR_EMPTY);
        // 是否从Map中获取id
        boolean getIdFromMap = ArrayUtils.contains(jsonConvertFeatureEnums, JsonConvertFeatureEnum.GET_ID_FROM_MAP);
        String key;
        Object value;
        for (Map.Entry entry : jsonObject.entrySet()) {
            key = entry.getKey().toString();
            value = entry.getValue();
            if (value == null && ignoreNullOrEmpty) {
                // 忽略null值
                continue;
            }
            try {
                String firstLetter = key.substring(0, 1);
                key = key.replaceFirst(firstLetter, firstLetter.toLowerCase());
                Field field;
                if (value instanceof Map && getIdFromMap) {
                    try {
                        // 有对应的带Id后缀属性，则优先取该属性
                        field = entity.getClass().getDeclaredField( key + "Id");
                        key += "Id";
                    } catch (NoSuchFieldException ex) {
                        // 没有则按key取属性
                        field = entity.getClass().getDeclaredField(key);
                    }
                } else {
                    field = getField(entity.getClass(),key);
                    if(field == null){
                        continue;
                    }
                }
                if (field.isAnnotationPresent(TableId.class) && ignoreEntityPrimaryKey) {
                    // 忽略实体主键
                    continue;
                }
                if (value instanceof String && ignoreNullOrEmpty && StringUtils.isEmpty((String)value)) {
                    // 忽略空字符串
                    continue;
                }
                if (value instanceof Map && getIdFromMap && !field.getType().isAssignableFrom(Map.class)) {
                    // 从Map中获取ID
                    value = ((Map)value).get(idKeyInMap);
                    if (value == null) {
                        // 获取不到则跳过
                        continue;
                    }
                }
                String methodName = "set" + key.replaceFirst(firstLetter, firstLetter.toUpperCase());
                Method method = entity.getClass().getMethod(methodName, field.getType());
                if (value == null) {
                    method.invoke(entity, new Object[] { null });
                } else {
                    method.invoke(entity, ConvertUtil.convertTypeValue(value.toString(), field.getType()));
                }
            } catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | ParseException e) {
                // e.printStackTrace();
            }
        }

        return entity;
    }

    public void setIdKeyInMap(String idKeyInMap) {
        this.idKeyInMap = idKeyInMap;
    }

    /**
     * 设置转换特性，在后续调用 {@link #convert(JSONObject, Class)} 或 {@link #convert(JSONObject, T)}
     * 时适用对应特性。特性的具体描述参考 {@code JsonConvertFeatureEnum}
     *
     * @param jsonConvertFeatureEnums   转换特性数组
     */
    public void setJsonConvertFeatureEnums(JsonConvertFeatureEnum... jsonConvertFeatureEnums) {
        this.jsonConvertFeatureEnums = jsonConvertFeatureEnums;
    }

    /**
     * 获取属性
     * 注:包括父类
     * @param clazz
     * @param key
     **/
    public Field getField(Class clazz,String key)  {
        while (clazz != null){
            try {
                return clazz.getDeclaredField(key);
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }
}
